package api

import (
	"log"
	"strconv"
	"time"
)

const (
	MAX_SIZE = 10
	MIN_SIZE = 2
)

type Job struct {
	handle func()
}

func NewJob(handle func()) *Job {
	return &Job{
		handle,
	}
}

type Worker struct {
	id     string
	job    chan *Job
	quit   chan struct{}
	master *Master
}

func NewWorker(id string, master *Master) *Worker {
	return &Worker{
		id:     id,
		job:    make(chan *Job),
		quit:   make(chan struct{}),
		master: master,
	}
}
func (_this *Worker) Start() {
	go func() {
		for {
			_this.master.WorkerPool <- _this.job
			select {
			case job := <-_this.job:
				job.handle()
			case <-_this.quit:
				return
			}
		}
	}()
}
func (_this Worker) Stop() {
	go func() {
		_this.quit <- struct{}{}
	}()
}

type Master struct {
	workerSize int
	jobs       chan *Job
	workers    []*Worker
	WorkerPool chan chan *Job
}

func NewMaster(workerSize int) *Master {
	if workerSize <= 0 {
		workerSize = MIN_SIZE
	} else if workerSize >= MAX_SIZE {
		workerSize = MAX_SIZE
	}
	return &Master{
		workerSize: workerSize,
		jobs:       make(chan *Job, 2*workerSize),
		WorkerPool: make(chan chan *Job, workerSize),
	}
}

func (_this *Master) Run() {
	for i := 0; i < _this.workerSize; i++ {
		worker := NewWorker("worker_id_"+strconv.Itoa(i+1), _this)
		_this.workers = append(_this.workers, worker)
		worker.Start()
	}
	go _this._dispatch()
	time.Sleep(time.Second)
}

func (_this *Master) _dispatch() {
	for job := range _this.jobs {
		go func(_job *Job) {
			J := <-_this.WorkerPool
			J <- _job
		}(job)
	}
}

func (_this *Master) AddJob(handle func()) {
	if handle == nil {
		log.Fatal("handle is nil")
		return
	}
	job := NewJob(handle)
	_this.jobs <- job
}

func (_this *Master) Stop() {
	for _, worker := range _this.workers {
		worker.Stop()
	}
}
